/**
 * Copyright 2019-2020 Huawei Technologies Co., Ltd
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "adapter/tbe_adapter/kernel_launch/tbe_kernel_launch.h"
#include "common/aicore_util_attr_define.h"
#include "common/comm_error_codes.h"
#include "common/comm_log.h"
#include "runtime/rt_error_codes.h"
#include "runtime/mem.h"
#include "runtime/kernel.h"
#include "common/taskdown_common.h"
#include "cce/cce_opiddef.hpp"

namespace fe {

Status TbeKernelLaunch::DealKernelLaunch(const ge::Node &node, ccHandle_t &handle, rtStream_t stream,
                                         const void *args, const uint32_t &args_size, const void *stub_dev_func,
                                         const uint32_t &core_dim) {
  string op_name = node.GetName();
  string op_type = node.GetType();
  auto op_desc = node.GetOpDesc();
  // 1. malloc  args_size + append_args_size
  void *all_args_buff = nullptr;
  size_t append_args_size = GetAppendArgsSizeOf() * GetAppendArgsNum();
  uint32_t total_args_size = args_size + append_args_size;
  if (rtMallocHost(&all_args_buff, total_args_size) != ACL_RT_SUCCESS) {
    return TASK_BUILDER_STATUS_INTERNAL_ERROR;
  }
  CM_CHECK_NOTNULL(all_args_buff);

  // 2. memcpy for args
  if (rtMemcpy(all_args_buff, args_size, args, args_size, RT_MEMCPY_HOST_TO_HOST) != SUCCESS) {
    if (rtFreeHost(all_args_buff) != ACL_RT_SUCCESS) {
      return TASK_BUILDER_STATUS_INTERNAL_ERROR;
    }
    all_args_buff = nullptr;
    return TASK_BUILDER_STATUS_INTERNAL_ERROR;
  }

  // 3. ccSetKernelContext
  AttrList attr_list;
  Status set_result = ccSetKernelContext(handle, CCE_DNN_OP_CONV_TE, attr_list, false, TE, nullptr);
  if (set_result != SUCCESS) {
    if (rtFreeHost(all_args_buff) != ACL_RT_SUCCESS) {
      return TASK_BUILDER_STATUS_INTERNAL_ERROR;
    }
    return TASK_BUILDER_STATUS_RUNTIME_ERROR;
  }

  // 4. memcpy form append_args
  if (AddAppendArgs(node, all_args_buff, args_size) != SUCCESS) {
    return TASK_BUILDER_STATUS_INTERNAL_ERROR;
  }

  // 5. call rtKernelLaunch
  rtError_t rtRet = ACL_RT_SUCCESS;
  CM_LOGD("Op[name=%s,type=%s]: args_size:%d bytes, append_args_size:%d bytes, total_args_size:%d bytes.",
          op_name.c_str(), op_type.c_str(), args_size, append_args_size, total_args_size);
  if (append_args_size > 0) {
    PrintAllArgs(op_name, op_type, all_args_buff, args_size);
  }

  string first_kernel_name;
  if (ge::AttrUtils::GetStr(op_desc, ATTR_NAME_KERNEL_LIST_FIRST_NAME, first_kernel_name)) {
    CM_LOGD("Node name is[%s], first kernel name is[%s].", op_name.c_str(), first_kernel_name.c_str());
    rtRet = rtKernelLaunchWithHandle(nullptr, stub_dev_func, core_dim, all_args_buff, total_args_size, nullptr, stream,
                                     nullptr);
  } else {
    rtRet = rtKernelLaunch(stub_dev_func, core_dim, all_args_buff, total_args_size, nullptr, stream);
  }

  if (rtRet != ACL_RT_SUCCESS) {
    if (rtFreeHost(all_args_buff) != ACL_RT_SUCCESS) {
      return TASK_BUILDER_STATUS_INTERNAL_ERROR;
    }
    return TASK_BUILDER_STATUS_RUNTIME_ERROR;
  }

  // 6.free all_args_buff
  if (rtFreeHost(all_args_buff) != ACL_RT_SUCCESS) {
    return TASK_BUILDER_STATUS_INTERNAL_ERROR;
  }
  all_args_buff = nullptr;
  return SUCCESS;
}

void TbeKernelLaunch::PrintAllArgs(const string &op_name, const string &op_type, const void *all_args_buff,
                                   uint32_t args_size) {
  for (size_t i = 0; i != args_size / sizeof(uint64_t); ++i) {
    uint64_t value = *(reinterpret_cast<uint64_t *>(reinterpret_cast<uintptr_t>(all_args_buff) + i * sizeof(uint64_t)));
    CM_LOGD("Op[name=%s,type=%s]: args[%zu]=[%d].", op_name.c_str(), op_type.c_str(), i, value);
  }

  for (size_t i = 0; i != GetAppendArgsNum(); ++i) {
    uint64_t value = *(reinterpret_cast<uint64_t *>(reinterpret_cast<uintptr_t>(all_args_buff) + args_size +
                                                    i * GetAppendArgsSizeOf()));
    CM_LOGD("Op[name=%s,type=%s]: append_args[%zu]=[%d].", op_name.c_str(), op_type.c_str(), i, value);
  }
}

size_t TbeKernelLaunch::GetAppendArgsSizeOf() { return 0; }
size_t TbeKernelLaunch::GetAppendArgsNum() { return 0; }
Status TbeKernelLaunch::AddAppendArgs(const ge::Node &node, void *all_args_buff, const uint32_t &args_size) {
  return SUCCESS;
}
}  // namespace fe
